
package services;

import cae.EnumStatus;
import cae.EnumVerdicts;
import cae.WindowsCAE;
import dao.SubmissionJpaController;
import dao.exceptions.NonexistentEntityException;
import entities.Submission;
import exceptions.*;
import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import util.EnumSearchParameter;
import vo.VOContest;
import vo.VOProblem;
import vo.VOSubmission;
import vo.VOSubmissionOutput;
import vo.VOTestcase;
import vo.VOUser;

/**
 *
 * @author sbaldrich
 */
public class SubmissionService {

    private static SubmissionJpaController sc;
    public static SubmissionJpaController getInstance()
    {
        if(sc == null)
            sc = new SubmissionJpaController();
        return sc;
    }

    private static ContestService cs;
    public static ContestService getContestService()
    {
        if(cs == null)
            cs = new ContestService();
        return cs;
    }

    private static ProblemService ps;
    public static ProblemService getProblemService()
    {
        if(ps == null)
            ps = new ProblemService();
        return ps;
    }

    private static UserService us;
    public static UserService getUserService()
    {
        if(us == null)
            us = new UserService();
        return us;
    }

    private static TestcaseService ts;
    public static TestcaseService getTestcaseService()
    {
        if(ts == null)
            ts = new TestcaseService();
        return ts;
    }

    public void save(VOSubmission vo) throws Exception
    {
        sc = getInstance();
        cs = getContestService();
        ps = getProblemService();
        us = getUserService();

        Submission s = new Submission(vo);
        try
        {
            s.setContest(cs.findObject(vo.getContestCode()));
            s.setUser(us.findObject(vo.getUserHandle()));
            s.setProblem(ps.findObject(vo.getProblemCode()));
            sc.create(s);
        } catch (Exception e)
        {
            if(e instanceof NonExistentObjectException)
                throw new NonExistentObjectException();
            throw new DatabaseException("SubmissionService.save");
        }
    }

    public VOSubmission find(VOSubmission vo)throws Exception
    {
        sc = getInstance();
        try
        {
            return sc.findSubmission(vo.getIdSubmission()).getVO();
        } catch (Exception e)
        {
            throw e;
        }
    }
    
    public void update(VOSubmission vo)throws Exception
    {
        sc = getInstance();
        cs = getContestService();
        ps = getProblemService();
        us = getUserService();
        Submission s = new Submission(vo);
        try
        {
            s.setContest(cs.findObject(vo.getContestCode()));
            s.setProblem(ps.findObject(vo.getProblemCode()));
            s.setUser(us.findObject(vo.getUserHandle()));
            sc.edit(s);
        } catch (Exception e)
        {
            if(e instanceof NonexistentEntityException)
            {
                throw new NonExistentObjectException();
            }
            throw new DatabaseException("SubmissionService.update Operation");
        }
    }

    public void delete(VOSubmission vo)throws Exception
    {
        sc = getInstance();
        try
        {
            sc.destroy(vo.getIdSubmission());
        } catch (Exception e)
        {
            if( e instanceof NonexistentEntityException)
            {
                throw new NonExistentObjectException();
            }
            throw new DatabaseException("SubmissionService.delete Operation");
        }
    }

    public VOSubmissionOutput processSubmission(VOSubmission vo) throws Exception
    {
        ts = getTestcaseService();
        if(!vo.getContestCode().isEmpty())
        {
            cs = getContestService();
            if(cs.find(vo.getContestCode()).getEnd().before(vo.getDate()))
                throw new Exception("Contest is not Active");
        }

        ArrayList<VOTestcase> testcases = ts.find(vo.getProblemCode());
        try
        {
            VOSubmissionOutput out = judgeSubmission(vo , testcases); 
            vo.setVerdict(out.getVerdict().value());
            save(vo);
            return out;
        }
        catch (Exception e)
        {
            throw e;
        }
    }

    private VOSubmissionOutput judgeSubmission(VOSubmission submission, ArrayList<VOTestcase> testCases)throws CompilationException
    {
        WindowsCAE cae = new WindowsCAE(3/*aquí va el time limit del problema o el problema*/,testCases,submission);
        String compileOutput = EnumStatus.NOT_COMPILED.value();
        String executionOutput = EnumStatus.NOT_COMPILED.value();
        EnumVerdicts verdict = EnumVerdicts.NOT_YET_COMPILED;
        EnumStatus status = EnumStatus.NOT_COMPILED;
        int executionTimeInMiliseconds = -1;
        cae.compileScript();
        if( cae.isSuccessfullyCompiled() )
        {
            cae.runTestCases();
            /*
            System.out.println("COMPILACION exitosa");
            System.out.println("COMPILATION OUTPUT:");
            System.out.println(cae.getCompilationOutput());
            System.out.println("EXECUTION OUTPUT:");
            System.out.println(cae.getExecutionOutput());
            System.out.println("VERDICT:");
            System.out.println(cae.getVerdict().value());
            System.out.println("Total Time: "+cae.getMaxExcecutionTimeInMiliSeconds()+" miliseconds");
            */

            executionOutput =cae.getExecutionOutput();
            verdict = cae.getVerdict();
            executionTimeInMiliseconds = cae.getMaxExcecutionTimeInMiliSeconds();
        }
        else
        {
            //System.out.println("COMPILATION ERROR");
            System.out.println(cae.getCompilationOutput());
        }
        try {
            WindowsCAE.killProcess("java.exe");
            WindowsCAE.killProcess("a.exe");
        } catch (Exception ex) {
            Logger.getLogger(SubmissionService.class.getName()).log(Level.SEVERE, null, ex);
        }
        status = EnumStatus.SUCCESSFULLY_JUDGED;
        compileOutput = cae.getCompilationOutput();
        return new VOSubmissionOutput(  compileOutput ,  executionOutput ,  verdict , status , executionTimeInMiliseconds );
    }


    public ArrayList<VOSubmission> findAll(EnumSearchParameter parameter, Object vo)throws Exception
    {
        sc = getInstance();
        us = getUserService();
        ps = getProblemService();
        try
        {
            Object o = null;
            if( vo instanceof VOUser)
                o = (Object)us.findObject(((VOUser)(vo)).getHandle());
            else if(vo instanceof VOProblem)
                o = (Object)ps.findObject(((VOProblem)(vo)).getProblemCode());
            else if(vo instanceof VOContest)
                o = (Object)ps.findObject(((VOContest)(vo)).getContestCode());

            else if ( o == null)
                throw new NonExistentObjectException();
            return sc.findAll(parameter , o);
        }
        catch (Exception e)
        {
            if(e instanceof DatabaseException)
                throw e;
            throw new InvalidParameterException();
            
        }
    }
    
}
